package com.hundsun.epay.autosimu.api.dom.java;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.StringTokenizer;

import com.hundsun.epay.autosimu.utils.StringUtility;

public class FullyQualifiedJavaType implements Comparable<FullyQualifiedJavaType> {
	//static field
	private static final String JAVA_LANG = "java.lang";
	
	private static FullyQualifiedJavaType intInstance;
	private static FullyQualifiedJavaType stringInstance;
	private static FullyQualifiedJavaType booleanInstance;
	private static FullyQualifiedJavaType bigDecimalInstance;
	private static FullyQualifiedJavaType objectInstance;
	private static FullyQualifiedJavaType dateInstance;
	
	private String baseShortName;   //基础类型名，无泛型参数
	private String baseQualifiedName;   
	private String packageName;   //包名
	
	private boolean isArray;   //是否是数组
	private boolean isPrimitiveType;   //是否是原始类型
	private boolean explicityImported;   //是否需要明确import
	private PrimitiveTypeWrapper primitiveTypeWrapper;   //原始类型包装器
	
	private List<FullyQualifiedJavaType> typeArguments;   //类型参数
	private boolean wildcard;   //通配符
	private boolean boundWildcard;    //界定通配符
	private boolean extendBoundWildcard;   //继承界定通配符
	
	public FullyQualifiedJavaType(String fullyQualifiedSpec){
		super();
		typeArguments = new ArrayList<FullyQualifiedJavaType>();
		parse(fullyQualifiedSpec);
	}
	
	private void parse(String fullyQualifiedSpec){
		String spec = fullyQualifiedSpec.trim();
		if(spec.startsWith("?")){  //类型通配符
			wildcard = true;
			spec = spec.substring(1).trim();   //截取通配符
			if(spec.startsWith("extends ")){   //继承通配，界定上线
				boundWildcard = true;
				extendBoundWildcard = true;
				spec = spec.substring(8);
				
			}else if(spec.startsWith("super ")){  //父类通配，界定下线
				boundWildcard = true;
				extendBoundWildcard = false;
				spec = spec.substring(6);
			}else{//无边界
				boundWildcard = false;
			}
			parse(spec);
		}else{  //无通配符
			int index = fullyQualifiedSpec.indexOf("<");   //判断泛型
			if(index  == -1){   //无泛型
				simpleParse(fullyQualifiedSpec);   
			}else{
				simpleParse(fullyQualifiedSpec.substring(0, index));  //解析普通类型
				int endIndex = fullyQualifiedSpec.indexOf(">");
				if(endIndex == -1){
					throw new RuntimeException(String.format("Invalid Type Specification: %s", fullyQualifiedSpec));
				}
				genericParse(fullyQualifiedSpec.substring(index, endIndex + 1));
			}
		}
		isArray = isArrayInternal(fullyQualifiedSpec);
	}
	
	private void simpleParse(String fullyQualifiedSpec){
		baseQualifiedName = fullyQualifiedSpec.trim();
		if(fullyQualifiedSpec.contains(".")){
			packageName = getPackage(fullyQualifiedSpec);
			baseShortName = baseQualifiedName.substring(packageName.length() + 1);
			
			if(JAVA_LANG.equals(packageName)){
				explicityImported = false;
			}else{
				explicityImported = true;
			}
		}else{
			baseShortName = baseQualifiedName;
			packageName = "";
			
			if("boolean".equals(baseShortName)){
				isPrimitiveType = true;
				primitiveTypeWrapper = PrimitiveTypeWrapper.getBooleanInstance();
			}else if("byte".equals(baseShortName)){
				isPrimitiveType = true;
				primitiveTypeWrapper = PrimitiveTypeWrapper.getByteInstance();
			}else if("char".equals(baseShortName)){
				isPrimitiveType = true;
				primitiveTypeWrapper = PrimitiveTypeWrapper.getCharacterInstance();
			}else if("double".equals(baseShortName)){
				isPrimitiveType = true;
				primitiveTypeWrapper = PrimitiveTypeWrapper.getDoubleInstance();
			}else if("float".equals(baseShortName)){
				isPrimitiveType = true;
				primitiveTypeWrapper = PrimitiveTypeWrapper.getFloatInstance();
			}else if("int".equals(baseShortName)){
				isPrimitiveType = true;
				primitiveTypeWrapper = PrimitiveTypeWrapper.getIntegerInstance();
			}else if("long".equals(baseShortName)){
				isPrimitiveType = true;
				primitiveTypeWrapper = PrimitiveTypeWrapper.getLongInstance();
			}else if("short".equals(baseShortName)){
				isPrimitiveType = true;
				primitiveTypeWrapper = PrimitiveTypeWrapper.getShortInstance();
			}else{
				isPrimitiveType = false;
				primitiveTypeWrapper = null;
			}
		}
	}
	
	private void genericParse(String fullyQualifiedSpec){
		int endIndex = fullyQualifiedSpec.lastIndexOf(">");
		if(endIndex == -1){
			throw new RuntimeException(String.format("Invalid Type Specification: %s", fullyQualifiedSpec));
		}
		String argumentsString = fullyQualifiedSpec.substring(1, endIndex);
		StringTokenizer st = new StringTokenizer(argumentsString, ",<>", true);
		StringBuilder sb = new StringBuilder();
		int openCount = 0;
		while(st.hasMoreTokens()){
			String token = st.nextToken();
			if("<".equals(token)){
				sb.append(token);
				openCount ++;
			}else if(">".equals(token)){
				sb.append(token);
				openCount --;
			}else if(",".equals(token)){
				if(openCount == 0){
					typeArguments.add(new FullyQualifiedJavaType(sb.toString()));
					sb.setLength(0);
				}else{
					sb.append(token);
				}
			}else{
				sb.append(token);
			}
		}
		
		if(openCount != 0){
			throw new RuntimeException(String.format("Invalid Type Specification: %s", fullyQualifiedSpec));
		}
		String finalType = sb.toString();
		if(StringUtility.stringHasValue(finalType)){
			typeArguments.add(new FullyQualifiedJavaType(finalType));
		}
	}
	
	/**
	 * 不能支持全名称的内部类
	 * @param fullyQualifiedSpec
	 * @return
	 */
	private static String getPackage(String fullyQualifiedSpec){
		int index = fullyQualifiedSpec.lastIndexOf(".");
		return fullyQualifiedSpec.substring(0, index);
	}
	
	private boolean isArrayInternal(String fullyQualifiedSpec){
		if(!fullyQualifiedSpec.contains("]") && !fullyQualifiedSpec.contains("[")){
			return false;
		}
		if(fullyQualifiedSpec.replaceAll("\\s", "").endsWith("[]")){
			return true;
		}else if(fullyQualifiedSpec.endsWith("]") || fullyQualifiedSpec.endsWith("[")){
			throw new RuntimeException(String.format("Invalid Type Specification: %s", fullyQualifiedSpec));
		}else{
			return false;
		}
	}

	public static FullyQualifiedJavaType getIntInstance() {
		if(intInstance == null){
			intInstance = new FullyQualifiedJavaType("int");
		}
		return intInstance;
	}

	public static FullyQualifiedJavaType getStringInstance() {
		if(stringInstance == null){
			stringInstance = new FullyQualifiedJavaType("java.lang.String");
		}
		return stringInstance;
	}

	public static FullyQualifiedJavaType getBooleanInstance() {
		if(booleanInstance == null){
			booleanInstance = new FullyQualifiedJavaType("boolean");
		}
		return booleanInstance;
	}

	public static FullyQualifiedJavaType getBigDecimalInstance() {
		if(bigDecimalInstance == null){
			bigDecimalInstance = new FullyQualifiedJavaType("java.math.BigDecimal");
		}
		return bigDecimalInstance;
	}

	public static FullyQualifiedJavaType getObjectInstance() {
		if(objectInstance == null){
			objectInstance = new FullyQualifiedJavaType("java.lang.Object");
		}
		return objectInstance;
	}

	public static FullyQualifiedJavaType getDateInstance() {
		if(dateInstance == null){
			dateInstance = new FullyQualifiedJavaType("java.util.Date");
		}
		return dateInstance;
	}
	
	public static FullyQualifiedJavaType getNewIntInstance() {
		return new FullyQualifiedJavaType("int");
	}

	public static FullyQualifiedJavaType getNewStringInstance() {
		return new FullyQualifiedJavaType("java.lang.String");
	}

	public static FullyQualifiedJavaType getNewBooleanInstance() {
		return new FullyQualifiedJavaType("boolean");
	}

	public static FullyQualifiedJavaType getNewBigDecimalInstance() {
		return new FullyQualifiedJavaType("java.math.BigDecimal");
	}

	public static FullyQualifiedJavaType getNewObjectInstance() {
		return new FullyQualifiedJavaType("java.lang.Object");
	}

	public static FullyQualifiedJavaType getNewDateInstance() {
		return new FullyQualifiedJavaType("java.util.Date");
	}

	public String getShortName() {
		StringBuilder sb = new StringBuilder();
		if(wildcard){
			sb.append("?");
			if(boundWildcard){
				if(extendBoundWildcard){
					sb.append(" extends ");
				}else{
					sb.append(" super ");
				}
				sb.append(baseShortName);
			}
		}else{
			sb.append(baseShortName);
		}
		
		if(!typeArguments.isEmpty()){
			sb.append("<");
			boolean first = true;
			for (FullyQualifiedJavaType typeArgument : typeArguments) {
				if(first){
					first = false;
				}else{
					sb.append(",");
				}
				sb.append(typeArgument.getShortName());
			}
			sb.append(">");
		}
		return sb.toString();
	}
	
	public String getShortNameWithoutTypeParameters(){
		return baseShortName;
	}

	public String getFullyQualifiedName() {
		StringBuilder sb = new StringBuilder();
		if(wildcard){
			sb.append("?");
			if(boundWildcard){
				if(extendBoundWildcard){
					sb.append(" extends ");
				}else{
					sb.append(" super ");
				}
				sb.append(baseQualifiedName);
			}
		}else{
			sb.append(baseQualifiedName);
		}
		
		if(!typeArguments.isEmpty()){
			sb.append("<");
			boolean first = true;
			for (FullyQualifiedJavaType typeArgument : typeArguments) {
				if(first){
					first = false;
				}else{
					sb.append(",");
				}
				sb.append(typeArgument.getFullyQualifiedName());
			}
			sb.append(">");
		}
		return sb.toString();
	}
	
	public String getFullyQualifiedNameWithoutTypeParameters() {
		return baseQualifiedName;
	}
	
	public List<String> getImportList(){
		List<String> importList = new ArrayList<String>();
		if(explicityImported){
			importList.add(calculateActualImportedName());
		}
		
		for (FullyQualifiedJavaType typeArgument : typeArguments) {
			importList.addAll(typeArgument.getImportList());
		}
		return importList;
	}
	
	private String calculateActualImportedName(){
		if(isArray){
			int index = baseQualifiedName.indexOf("[");
			return baseQualifiedName.substring(0, index);
		}
		return baseQualifiedName;
	}

	public String getPackageName() {
		return packageName;
	}

	public boolean isArray() {
		return isArray;
	}

	public boolean isPrimitiveType() {
		return isPrimitiveType;
	}

	public boolean isExplicityImported() {
		return explicityImported;
	}

	public PrimitiveTypeWrapper getPrimitiveTypeWrapper() {
		return primitiveTypeWrapper;
	}

	public List<FullyQualifiedJavaType> getTypeArguments() {
		return typeArguments;
	}

	public boolean isWildcard() {
		return wildcard;
	}

	public boolean isBoundWildcard() {
		return boundWildcard;
	}

	public boolean isExtendBoundWildcard() {
		return extendBoundWildcard;
	}
	
	public void addTypeArguments(FullyQualifiedJavaType typeArgument){
		this.typeArguments.add(typeArgument);
	}

	@Override
	public int hashCode() {
		return getFullyQualifiedName().hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if(this == obj){
			return true;
		}
		if(!(obj instanceof FullyQualifiedJavaType)){
			return false;
		}
		FullyQualifiedJavaType sec = (FullyQualifiedJavaType) obj;
		return this.getFullyQualifiedName().equals(sec.getFullyQualifiedName());
	}

	@Override
	public String toString() {
		return this.getFullyQualifiedName();
	}

	@Override
	public int compareTo(FullyQualifiedJavaType o) {
		return this.getFullyQualifiedName().compareTo(o.getFullyQualifiedName());
	}
}
